#include <iostream>
#include <fstream>
#include "q4.h"

using namespace std;

// constructor
Q4::Q4(list<Structures::process> p){
	processes = p;

	Structures::processor p1('A', 0, 1);
	Structures::processor p2('B', 0, 1);
	Structures::processor p3('C', 0, 1);
	Structures::processor p4('D', 0, 1);
	Structures::processor p5('E', 0, 1);

	p1.timeLeft = 0; p1.cyclesRemaining = 0;
	p2.timeLeft = 0; p2.cyclesRemaining = 0;
	p3.timeLeft = 0; p3.cyclesRemaining = 0;
	p4.timeLeft = 0; p4.cyclesRemaining = 0;
	p5.timeLeft = 0; p5.cyclesRemaining = 0;

	processors.push_back(p1);
	processors.push_back(p2);
	processors.push_back(p3);
	processors.push_back(p4);
	processors.push_back(p5);

	tick = 1;
	tick_count = 0;
	probability = 3000;

	num_finished = 0;
	num_processes = processes.size();
}

// schedule processes
void Q4::schedule(){

	cout << "Simulating real-time process scheduling..." << endl;

	srand((unsigned int)time(NULL));

	// Keep running until all processes are finished
	while(num_finished < num_processes){

		// probability of new process arrival (if there are more processes remaining to be scheduled)
		if(!processes.empty() && rand() % probability == 0){

			// get first process
			Structures::process minProcess = *processes.begin();
			processes.pop_front();

			// reset tick count to zero when first process starts
			if(minProcess.pid == 0)
				tick_count = 0;

			//cout << "Tick " << tick_count << ": " << "Arrival of process " << minProcess.pid << endl;

			double currMinTime = INT_MAX;

			list<Structures::processor>::iterator minTimeProcessor = processors.begin();

			// find processor which would minimize execution time across all processors
			for(list<Structures::processor>::iterator j = processors.begin(); j != processors.end(); j++){
				// decrement cycles left on process and processor
				if(!((*j).processes.empty())){			
					if((*j).processes.front().burst <= tick){
						//cout << "Tick " << tick_count << ": " << "Process " << (*j).processes.front().pid << " is finished" << endl;
						(*j).cyclesRemaining -= (*j).processes.front().burst;
						(*j).processes.pop_front();
						num_finished++;
					}
					else{
						(*j).processes.front().burst -= tick;
						(*j).cyclesRemaining -= tick;
					}
				}
			
				if((minProcess.burst + (*j).cyclesRemaining)/(double)(*j).speed < currMinTime){
					currMinTime = (minProcess.burst + (*j).cyclesRemaining)/(double)(*j).speed;			
					minTimeProcessor = j;
				}
			}

			// if currently executing process has less time left
			if( (*minTimeProcessor).processes.empty() || (*minTimeProcessor).processes.front().burst < minProcess.burst){
				// add the process to the back
				(*minTimeProcessor).processes.push_back(minProcess);
				(*minTimeProcessor).cyclesRemaining += minProcess.burst;
			}
			// if new process requires less time than currently executing process
			else{
				// add the process to the front
				(*minTimeProcessor).processes.push_front(minProcess);
				(*minTimeProcessor).cyclesRemaining += minProcess.burst;
			}
		}

		// if no new process is arriving, decrement cycles
		else{
			for(list<Structures::processor>::iterator j = processors.begin(); j != processors.end(); j++){
				// decrement cycles left on process and processor
				if(!((*j).processes.empty())){			
					if((*j).processes.front().burst <= tick){
						//cout << "Tick " << tick_count << ": " << "Process " << (*j).processes.front().pid << " is finished" << endl;
						(*j).cyclesRemaining -= (*j).processes.front().burst;
						(*j).processes.pop_front();
						num_finished++;
					}
					else{
						(*j).processes.front().burst -= tick;
						(*j).cyclesRemaining -= tick;
					}
				}
			}
		}
		tick_count++;
	}

}

// print list
void Q4::print(){
	ofstream outfile;
	outfile.open ("question4_output.txt");

	outfile << "Question 4: Sequential arrival of processes" << endl << endl;

	/*
	processors.sort(Structures::compareProcessorID);
	// print out scheduled processes for each processor
	for(int i = 0; i < 5; i++){
	
		(*processors.begin()).processes.sort(Structures::compareProcessCycles); //sort each processor's queue SJF
		
		outfile << "Processor " << (*processors.begin()).processorid << endl;
		outfile << "\tPID:\tBurst (x1,000,000):\tMemory (in KB):" << endl;
		outfile << "----------------------------------------------------------" << endl;

		int num = 1;
		
		for(list<Structures::process>::iterator i = (*processors.begin()).processes.begin(); i != (*processors.begin()).processes.end(); i++)
		{
			outfile << num << ".\t" << (*i).pid << "\t" << (*i).burst;
			num++;
			if((*i).burst < 10000000) //extra tab for shorter numbers
			{
				outfile << "\t";
			}
			outfile	<< "\t\t" << (*i).memoryReq << endl;
		}
		outfile << "Total cycles scheduled: " << (*processors.begin()).cyclesRemaining << " (x1,000,000)" << endl << endl;
		processors.pop_front();
	}
	outfile.close();
	*/
	outfile << "Tick = " << tick << " million cycles" << endl;
	outfile << "Probability (at each tick) of arrival of next process: 1/" << probability << endl;
	outfile << "Cycles (x1,000,000) taken to complete all " << num_processes << " processes: " << tick_count << endl; 
}